package utils

import (
	"common/config"
	"context"
	"fmt"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

type MongoStruct struct {
	client   *mongo.Client
	database *mongo.Database
}

func NewMongoClient() (*MongoStruct, error) {
	conf, _ := config.LoadMongoConfig()
	MongoUrl := fmt.Sprintf("mongodb://%v:%v@%v:%d", conf.User, conf.Password, conf.Host, conf.Port)
	client, err := mongo.Connect(context.Background(), options.Client().ApplyURI(MongoUrl))

	if err != nil {
		return nil, err
	}
	err = client.Ping(context.Background(), nil)
	if err != nil {
		Logger().Infoln(err.Error())
	}

	return &MongoStruct{
		client:   client,
		database: client.Database(conf.Db),
	}, nil

}

func (m *MongoStruct) InsertOne(collection string, data map[string]interface{}) interface{} {
	result, _ := m.database.Collection(collection).InsertOne(context.Background(), data)
	return result.InsertedID
}

func (m *MongoStruct) InsertMany(collection string, data []interface{}) []interface{} {
	result, _ := m.database.Collection(collection).InsertMany(context.Background(), data)
	return result.InsertedIDs
}

func (m *MongoStruct) Delete(collection string, filter filter) int64 {
	result, _ := m.database.Collection(collection).DeleteMany(context.Background(), filter)
	return result.DeletedCount
}

func (m *MongoStruct) DeleteOneById(collection string, id string) int64 {
	result, _ := m.database.Collection(collection).DeleteOne(context.Background(), filter{{Key: "_id", Value: m.ObjectID(id)}})
	return result.DeletedCount
}

func (m *MongoStruct) FindOne(collection string, filter filter) map[string]any {
	var result bson.M
	cursor := m.database.Collection(collection).FindOne(context.Background(), filter)
	err := cursor.Decode(&result)
	if err != nil {
		return make(map[string]any, 0)
	}
	return result
}

func (m *MongoStruct) FindOneById(collection string, id string) map[string]any {
	var result bson.M
	cursor := m.database.Collection(collection).FindOne(context.Background(), filter{{Key: "_id", Value: m.ObjectID(id)}})
	err := cursor.Decode(&result)
	if err != nil {
		return make(map[string]any, 0)
	}
	return result
}

func (m *MongoStruct) FindMany(collection string, filter filter, opts ...*options.FindOptions) ([]bson.M, error) {
	cursor, _ := m.database.Collection(collection).Find(context.Background(), filter, opts...)
	defer cursor.Close(context.Background())

	var results []bson.M
	for cursor.Next(context.Background()) {
		var result bson.M
		err := cursor.Decode(&result)
		if err != nil {
			return nil, err
		}

		results = append(results, result)
	}
	if err := cursor.Err(); err != nil {
		return nil, err
	}
	return results, nil

}

func (m *MongoStruct) UpdateOne(collection string, filter filter, update interface{}) int64 {
	result, _ := m.database.Collection(collection).UpdateOne(context.Background(), filter, bson.M{"$set": update})
	return result.ModifiedCount
}

func (m *MongoStruct) UpdateOneById(collection string, id string, update interface{}) int64 {
	result, _ := m.database.Collection(collection).UpdateOne(context.Background(), filter{{Key: "_id", Value: m.ObjectID(id)}}, bson.M{"$set": update})
	return result.ModifiedCount
}

func (m *MongoStruct) UpdateMany(collection string, filter filter, update interface{}) int64 {
	result, _ := m.database.Collection(collection).UpdateMany(context.Background(), filter, bson.D{{Key: "$set", Value: update}})
	return result.ModifiedCount
}

func (m *MongoStruct) ObjectID(id string) primitive.ObjectID {
	objectId, _ := primitive.ObjectIDFromHex(id)
	return objectId
}

// 定义过滤器
type filter bson.D

func NewFilter() filter {
	return filter{}
}

func (f filter) GT(key string, value interface{}) filter {
	return append(f, bson.E{Key: key, Value: bson.D{{Key: "$gt", Value: value}}})
}

func (f filter) GTE(key string, value interface{}) filter {
	return append(f, bson.E{Key: key, Value: bson.D{{Key: "$gte", Value: value}}})
}

func (f filter) EQ(key string, value interface{}) filter {
	return append(f, bson.E{Key: key, Value: bson.D{{Key: "$eq", Value: value}}})
}

func (f filter) LT(key string, value interface{}) filter {
	return append(f, bson.E{Key: key, Value: bson.D{{Key: "$lt", Value: value}}})
}

func (f filter) LET(key string, value interface{}) filter {
	return append(f, bson.E{Key: key, Value: bson.D{{Key: "$let", Value: value}}})
}

func (f filter) NE(key string, value interface{}) filter {

	return append(f, bson.E{Key: key, Value: bson.D{{Key: "$ne", Value: value}}})
}

func (f filter) IN(key string, value ...interface{}) filter {
	return append(f, bson.E{Key: key, Value: bson.D{{Key: "$in", Value: value}}})
}

func (f filter) NIN(key string, value ...interface{}) filter {

	return append(f, bson.E{Key: key, Value: bson.D{{Key: "$nin", Value: value}}})
}
